using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Utilities;
using PostscriptTools.Classifier;

namespace PostscriptTools.Intellisense {
	[Export(typeof(ICompletionSourceProvider))]
	[ContentType("postscript")]
	[Name("postscriptTokenCompletion")]
	[Order(Before = "default")]
	class PostscriptTokenCompletionSourceProvider : ICompletionSourceProvider {
		public ICompletionSource TryCreateCompletionSource(ITextBuffer textBuffer) {
			return new PostscriptTokenCompletionSource(textBuffer);
		}
	}

	/// <summary>
	/// Provide list of valid string-substitution tokens when the user types CTRL-.
	/// </summary>
	class PostscriptTokenCompletionSource : ICompletionSource {
		private ITextBuffer _buffer;

		public PostscriptTokenCompletionSource(ITextBuffer buffer) {
			_buffer = buffer;
		}

		void IDisposable.Dispose() {
		}

		/// <summary>
		/// Provide a list of completion items that contains the list of identifiers used in the buffer, together with the line number(s) where they are defined
		/// </summary>
		public void AugmentCompletionSession(ICompletionSession session, IList<CompletionSet> completionSets) {
			// create a list of completions from the dictionary of valid tokens
			ITextSnapshot snapshot = _buffer.CurrentSnapshot;
			SnapshotPoint? triggerPoint = session.GetTriggerPoint(snapshot);
			if (triggerPoint == null) {
				return;
			}
			SnapshotPoint end = triggerPoint.Value;
			SnapshotPoint start = (end > 0) ? end - 1: end;
			// go back to either a delimiter, a whitespace char or start of line.
			while (start > 0){
				SnapshotPoint prev = start - 1;
				if (LineProgress.IsWhiteSpaceOrDelimiter(prev.GetChar())){
					break;
				}
				start += -1;
			}
			// postscript code: /abc          /name {abc
			//                   ^start              ^start
			//                     ^end                ^end
			var span = new SnapshotSpan(start, end);
			// The ApplicableTo span is what text will be replaced by the completion item
			ITrackingSpan applicableTo = snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeInclusive);
			string currenttext = snapshot.GetText(span);
			List<Completion> completions = new List<Completion>();
			foreach (KeyValuePair<string, string> token in PostscriptTokenTagger.DefinedPostscriptNames.ToList()) {  // the ToList is there to avoid the exception "collection was modified enumeration may not execute" 
				// if the text from the applicableTo matches the beginning of the defined names, add it to the completions
				if (token.Key.StartsWith(currenttext)) {  // empty string is fine
					completions.Add(new Completion(token.Key, token.Key, token.Value, null, null));
				}
			}
			// and add the reserved names as possible completion members
			if (LineProgress.PredefinedPostscriptNames != null) {
				completions.Add(new Completion("------------", "", "List of matching reserved names below", null, null));
				foreach (KeyValuePair<string, string> token in LineProgress.PredefinedPostscriptNames) {
					if (token.Key.StartsWith(currenttext)) {  // empty string is fine
						completions.Add(new Completion(token.Key, token.Key, token.Value, null, null));
					}
				}
			}
			string moniker = "PostscriptCompletions";
			string displayName = "variables or operators";
			completionSets.Add(new CompletionSet(moniker, displayName, applicableTo, completions, Enumerable.Empty<Completion>()));
		}
	}
}

